#include <stdio.h>
#include "Messenger.h"


#define MUTE_L_LED      13
#define MUTE_R_LED      14

#define PGA_ZCEN_PIN     3
#define PGA_MUTE_PIN     2

#define PGA_CS_PIN      12
#define PGA_SDATA_PIN   11
#define PGA_SCK_PIN     10

// Gain (dB) = 31.5 − 0.5 * (255 − N)
#define UNITY_GAIN     192 //   0.0db
#define MIN_GAIN         1 // -95.5dB

#define BAUD_RATE   115200

Messenger message = Messenger(); 

int volume_left, old_l;
int volume_right, old_r;

void pga2311_init(void)
{
  pinMode(PGA_CS_PIN,    OUTPUT);    // pga-chipselect pin
  pinMode(PGA_SDATA_PIN, OUTPUT);    // pga-data-out (our data out to it) pin
  pinMode(PGA_SCK_PIN,   OUTPUT);    // pga-clock pin

  pinMode(PGA_ZCEN_PIN, OUTPUT);
}

void pga2311_enable_zero_crossing_detection()
{
  digitalWrite(PGA_ZCEN_PIN, HIGH);
}

void pga2311_disable_zero_crossing_detection()
{
  digitalWrite(PGA_ZCEN_PIN, LOW);
}

void SPI_write(uint8_t out_spi_byte )
{
  uint8_t  i;

  // loop thru each of the 8-bits in the byte, MSB first
  for (i=0; i < 8; i++) {

    // strobe clock
    digitalWrite(PGA_SCK_PIN, LOW);

    // send the bit (we look at the high order bit and 'print' that to the remote device)
    if (0x80 & out_spi_byte) {  // MSB is set
      digitalWrite(PGA_SDATA_PIN, HIGH);
    }
    else {
      digitalWrite(PGA_SDATA_PIN, LOW);
    }

    // unstrobe the clock via local SPI
    digitalWrite(PGA_SCK_PIN, HIGH);

    // get the next bit  
    out_spi_byte <<= 1;   // left-shift the byte by 1 bit
  }
}

void pga2311_mute()
{
  pga2311_set_volume(0, 0);  
}


void pga2311_set_volume( uint8_t left, uint8_t right )
{
  digitalWrite(PGA_CS_PIN, LOW);     // assert CS
  SPI_write(right);   // right value (0..255)
  SPI_write(left);    // left value (0..255)
  digitalWrite(PGA_CS_PIN, HIGH);    // deassert CS

  // activate mute indicator leds
  digitalWrite(MUTE_L_LED, LOW);
  digitalWrite(MUTE_R_LED, LOW);

  if (left  == 0) digitalWrite(MUTE_L_LED, HIGH);
  if (right == 0) digitalWrite(MUTE_R_LED, HIGH);

  // debugging output
  /*
  Serial.print(left, DEC);
  Serial.print(",");
  Serial.println(right, DEC);
  */
}


void messageReady() {
  bool left_selected = false;
  bool right_selected = false;


  char cmd=message.readChar();
  switch(cmd) {
  case 'l':
    left_selected = true;
    break;

  case 'r':
    right_selected = true;
    break;

  case 'b':
    left_selected = true;
    right_selected = true;
    break;
  }

  if (message.available()) {
    int volume = message.readInt();
    if (left_selected) {
      volume_left = volume;
    }
    if (right_selected) {
      volume_right = volume;
    }
  }
  else {
    // flush message when there's no match
    message.readChar();
  }

}


void setup()
{
  Serial.begin(BAUD_RATE);
  Serial.println("AUDIOVOLUME $Revision: 59 $, Ready!");

  pinMode(MUTE_L_LED, OUTPUT);
  pinMode(MUTE_R_LED, OUTPUT);

  // set muting
  pinMode(PGA_MUTE_PIN, OUTPUT);
  digitalWrite(PGA_MUTE_PIN, HIGH); // high -> not muted, low -> muted

  pga2311_init();

  // set zero crossing detection
  pga2311_enable_zero_crossing_detection();

  // make sure the volume is set to 0dB at startup
  volume_left = volume_right = UNITY_GAIN;
  old_l = old_r = 0;

  // Attach the callback function to the Messenger
  message.attach(messageReady);
}


void loop()                     
{
  while ( Serial.available() )  message.process(Serial.read () );

  if (volume_left != old_l || volume_right != old_r) {
    // set volume
    pga2311_set_volume(volume_left, volume_right);

    old_l = volume_left;
    old_r = volume_right;
  }
}






